#include "Gui.h"

// 选择目录函数
void SelectDirectory(wchar_t* selectedPath)
{
	BROWSEINFOW bi = { 0 };
	bi.hwndOwner = getHWnd();  // 添加窗口句柄
	bi.lpszTitle = L"请选择要操作的目录";
	LPITEMIDLIST pidl = SHBrowseForFolderW(&bi);

	if (pidl)
	{
		SHGetPathFromIDListW(pidl, selectedPath);
		CoTaskMemFree(pidl);
	}
}
// 创建帮助窗口
void ShowHelpWindow()
{
	HWND hwnd = getHWnd();
	const wchar_t* HELP_TEXT =
	{
		L"gui界面使用说明：\n\n"
		L"1. 点击【选择目录树】按钮选择需要操作的文件夹\n"
		L"2. 根据需求点击下方功能按钮：\n"
		L"   - 左侧列：打包/压缩/加密\n"
		L"   - 右侧列：解包/解压/解密\n"
		L"注:\n"
		L"   - 本程序[课设]主体针对目录树,故GUI界面不提供文件选择功能,所以执行其他功能前,必须打包\n"
		L"   - 左侧列功能执行了哪些,右侧列对应功能后续必须按其反序依次执行\n"
		L"   - 请勿随意跳转步骤,否则文件若无法恢复或生成,后果自负\n"
		L"3. 操作完成后会有弹窗提示【成功与否】\n"
		L"4. 生成的文件/文件夹与所选的目录树在同一目录(路径)下\n"
		L"5. 生成的文件/文件夹命名默认与选定目录树名有关，无法自行设定\n"
		L"6. 文件格式：\n"
		L"   - 打包文件：*.pack\n"
		L"   - 压缩/加密文件：*.lzw\n"
		L"7. 由于该gui界面旨在快捷方便地调用并展示主要功能，故提供功能较为死板，想要更加灵活的功能操作，请使用命令行界面\n\n"
		L"注意事项：\n"
		L"- 若想查看命令行界面使用方法，请在_终端_输入：该运行程序的路径+参数i\n"
		L"- 例：C:\\*.exe i 或 .\\*.exe i\n"
		L"- 请确保操作前备份重要数据，防止意外操作导致数据无法恢复\n"
		L"- 未选择目录时执行操作会给予报错提示\n"
		L"- 但更多错误反馈,如对应功能执行对象文件[夹]格式不匹配等问题,请结合命令行窗口[黑窗口]提示判断\n"
		L"- 加密/解密_密码字符_长度需小于等于100\n"
		L"- 本程序压缩为LZW算法压缩，故在处理重复性弱，模式不连续的文件时(如【.exe】等文件)，压缩质量会降低，同时伴有部分文件无法压缩而丢失的case，请注意甄别！\n"
	};

	MessageBoxW(NULL, HELP_TEXT, L"使用说明", MB_OK | MB_ICONINFORMATION);
}
// 绘制按钮
void DrawButton(Button btn)
{
//	setfillcolor(btn.pressed ? LIGHTGRAY : 0xDDDDDD);
	setfillcolor(btn.pressed ? LIGHTGRAY : DARKGRAY);
	bar(btn.x, btn.y, btn.x + BTN_WIDTH, btn.y + BTN_HEIGHT);

	setcolor(GREEN);
	rectangle(btn.x, btn.y, btn.x + BTN_WIDTH, btn.y + BTN_HEIGHT);

	setbkmode(TRANSPARENT);
	int textWidth = textwidth(btn.text);
	outtextxy(btn.x + (BTN_WIDTH - textWidth)/2,
	          btn.y + (BTN_HEIGHT - textheight(btn.text))/2,
	          btn.text);
}

// 创建密码输入窗口
PasswordWindow CreatePasswordWindow(int width, int height)
{
	PasswordWindow pw;

	pw.x = (getwidth() - width)/2;  // 居中显示
	pw.y = (getheight() - height)/2;
	pw.input[0] = '\0';
	pw.cursorPos = 0;
	pw.active = false;
	pw.confirm = false;

	return pw;
}
// 绘制密码输入窗口
void DrawPasswordWindow(PasswordWindow& pw)
{
	// 半透明背景
	setfillcolor(0x7F000000);
	bar(0, 0, getwidth(), getheight());

	// 窗口主体
	const int WINDOW_WIDTH = 300;
	const int WINDOW_HEIGHT = 180;

	setfillcolor(0xEEEEEE);
	bar(pw.x, pw.y, pw.x + WINDOW_WIDTH, pw.y + WINDOW_HEIGHT);

	setcolor(BLACK);
	rectangle(pw.x, pw.y, pw.x + WINDOW_WIDTH, pw.y + WINDOW_HEIGHT);

	// 标题
	setfont(16, 0, "宋体");
	outtextxy(pw.x + 20, pw.y + 20, "请输入密码:");

	// 输入框
	setfillcolor(WHITE);
	bar(pw.x + 20, pw.y + 50, pw.x + 280, pw.y + 80);
	rectangle(pw.x + 20, pw.y + 50, pw.x + 280, pw.y + 80);

	// 显示掩码（*号）
	std::string displayStr(pw.input);
	std::replace(displayStr.begin(), displayStr.end(), '\0', ' ');
	/* replace_手动替换_替代方案
	// for (auto& c : displayStr) {
	//     if (c == '\0') c = ' ';
	// }
	*/
	for (auto& c : displayStr) c = '*';
	outtextxy(pw.x + 25, pw.y + 55, displayStr.c_str());

	// 确定/取消按钮
	setfillcolor(0xCCCCCC);
	bar(pw.x + 60, pw.y + 120, pw.x + 140, pw.y + 150);
	bar(pw.x + 160, pw.y + 120, pw.x + 240, pw.y + 150);
	outtextxy(pw.x + 85, pw.y + 125, "确定");
	outtextxy(pw.x + 185, pw.y + 125, "取消");
}
// 处理密码窗口输入
bool ProcessPasswordWindow(PasswordWindow& pw)
{
	while (mousemsg())
	{
		mouse_msg msg = getmouse();
		if (msg.is_left() && msg.is_up())
		{
			// 检查确定按钮
			if (msg.x > pw.x + 60 && msg.x < pw.x + 140 &&
			        msg.y > pw.y + 120 && msg.y < pw.y + 150)
			{
				pw.confirm = true;
				pw.active = false;
				return true;
			}
			// 检查取消按钮
			if (msg.x > pw.x + 160 && msg.x < pw.x + 240 &&
			        msg.y > pw.y + 120 && msg.y < pw.y + 150)
			{
				pw.active = false;
				return false;
			}
		}
	}
	while (kbmsg())
	{
		key_msg key = getkey();
		if (key.msg == key_msg_char && pw.active)
		{
			if (key.key == 8)   // 退格键
			{
				if (pw.cursorPos > 0)
					pw.input[--pw.cursorPos] = '\0';
			}
			else if (pw.cursorPos < 63)
			{
				pw.input[pw.cursorPos++] = key.key;
				pw.input[pw.cursorPos] = '\0';
			}
		}
	}
	return false;
}

//kernel
void GUI_run()
{
	//窗口_init
	initgraph(800, 400);
	setbkcolor(0xDDDDDD);

	wchar_t selectedPath[MAX_PATH] = L"未选择目录"; //主要参数_Lambda
	// 计算布局参数
//	int CenterX = getwidth() / 2; --改用#define
	int leftColumnX = CenterX - BTN_WIDTH - COLUMN_SPACE/2;
	int rightColumnX = CenterX + COLUMN_SPACE/2;

	// 初始化按钮
	Button btnHelp = { MIDDLE, 300, "使用说明", 0 };	// 居中下
	Button btnSelect = { MIDDLE, 100, "选择目录树", 0 };  // 居中上
	Button btnPack = { leftColumnX, 150, "打包", 0 };
	Button btnCompress = { leftColumnX, 200, "lzw压缩", 0 };
	Button btnEncrypt = { leftColumnX, 250, "加密", 0 };
	Button btnUnpack = { rightColumnX, 150, "解包", 0 };
	Button btnUncompress = { rightColumnX, 200, "lzw解压", 0 };
	Button btnDecrypt = { rightColumnX, 250, "解密", 0 };

	CoInitialize(NULL);

	// 添加密码窗口变量
	PasswordWindow pwWindow;
	std::string password;
	bool needPassword = false;
	void (*pendingOperation)(wchar_t*, const char*) = nullptr;//Lambda

	for (; is_run(); delay_fps(60))
	{
		// 处理密码窗口
		if (pwWindow.active)
		{
			cleardevice();
			DrawPasswordWindow(pwWindow);
			if (ProcessPasswordWindow(pwWindow))
			{
				password = pwWindow.input;
				if (pendingOperation && !password.empty())
				{
					pendingOperation(selectedPath, password.c_str()),
					                 MessageBoxA(NULL, "操作已完成", "提示", MB_OK);
				}
				pwWindow.input[0] = '\0';
				pwWindow.cursorPos = 0;
			}
			continue; // 跳过主界面绘制
		}
		// 处理鼠标事件
		while (mousemsg())
		{
			mouse_msg msg = getmouse();
			if (msg.is_left())
			{
				//  定义一个lambda函数，用于检查按钮的点击，并执行相应的操作
				auto CheckButton = [&](Button& btn, void (*operation)(wchar_t*, const char*) )
				{
					if (msg.x > btn.x && msg.x < btn.x + BTN_WIDTH &&
					        msg.y > btn.y && msg.y < btn.y + BTN_HEIGHT)
					{
						btn.pressed = msg.is_down();
						if (msg.is_up())
						{
							if (&btn == &btnSelect)
								SelectDirectory(selectedPath);
							else if (&btn == &btnHelp)
								ShowHelpWindow();
							else
							{
								if (wcscmp(selectedPath, L"未选择目录") != 0)
								{
									if (operation == Encrypt ||
									        operation == Decrypt)
									{
										pendingOperation = operation;
										pwWindow = CreatePasswordWindow(300, 180);
										pwWindow.active = true;
									}
									else
										operation(selectedPath, ""),
										          MessageBoxA(NULL, "操作已完成", "提示", MB_OK);
								}
								else
									MessageBoxA(NULL, "请先选择目录", "错误", MB_OK);

							}
						}
					}
				};
				//按钮检测注册表
				CheckButton(btnHelp, nullptr), CheckButton(btnSelect, nullptr);
				CheckButton(btnPack, Pack), CheckButton(btnUnpack, Unpack);
				CheckButton(btnCompress, Compress), CheckButton(btnUncompress, Uncompress);
				CheckButton(btnEncrypt, Encrypt), CheckButton(btnDecrypt, Decrypt);
			}

		}

		// 绘制界面
		cleardevice();
		setcolor(BLUE);
		setfont(24, 0, "微软雅黑");
		outtextxy(centerX(textwidth("目录处理工具")), 30, "目录处理工具");

		setfont(12, 0, "宋体");
		DrawButton(btnHelp),DrawButton(btnSelect);
		DrawButton(btnPack),DrawButton(btnUnpack);
		DrawButton(btnCompress),DrawButton(btnUncompress);
		DrawButton(btnEncrypt),DrawButton(btnDecrypt);

		setcolor(CYAN);
		setfont(14, 0, "宋体");
		outtextxy(MARGIN, 350, "当前路径：");
		outtextxy(MARGIN + textwidth("当前路径："), 350, selectedPath);
	}

	CoUninitialize();
	closegraph();
}

//具体函数功能封装_API_interface
void Pack(wchar_t* path, const char* nul)
{
	// src_init
	wchar_t src[wcslen(path)];
	wcscpy(src,path);
	//source.pack
	wcscat(path,L".pack");
	//kernel
	pack_directory(src,path,'p');

}
void Unpack(wchar_t* path, const char* nul)
{
	/*
	// des_init
	int len = wcslen(path) - 5 ;
	wchar_t dest[len+4];
	wcsncpy(dest,path,len);
	wcscat(dest,L"_fin");
	//kernel
	unpack_directory(path,dest,'d');
	wcscpy(path,dest);
	*/
	// src_init
	wchar_t src[wcslen(path)];
	wcscpy(src,path);
	//*_fin
	wchar_t* last_slash = wcsrchr(path, L'.');
	if (last_slash)
	{
		*last_slash = L'_';
		*(++last_slash) = L'f';
		*(++last_slash) = L'i';
		*(++last_slash) = L'n';
		*(++last_slash) = L'\0';
	}
	else  //NULL
		wcscat(path,L"_fin\0");
	//kernel
	unpack_directory(src,path,'d');

}
void Compress(wchar_t* path, const char* nul)
{
	// src_init
	wchar_t src[wcslen(path)];
	wcscpy(src,path);

	//source.lzw
	wchar_t* last_slash = wcsrchr(path, L'.');
	if (last_slash)
	{
		*(++last_slash) = L'l';
		*(++last_slash) = L'z';
		*(++last_slash) = L'w';
		*(++last_slash) = L'\0';
	}
	else  //NULL
		wcscat(path,L".lzw");

	//kernel
	fileW_open(input,src,L"rb");
	fileW_open(output,path,L"wb");
	lzw_compress(input, output), Wfclose_all(src);
}
void Uncompress(wchar_t* path, const char* nul)
{
	// src_init
	wchar_t src[wcslen(path)];
	wcscpy(src,path);
	//_OUT.pack
	wchar_t* last_slash = wcsrchr(path, L'.');
	if (last_slash)
	{
		*last_slash = L'_';
		*(++last_slash) = L'O';
		*(++last_slash) = L'U';
		*(++last_slash) = L'T';
		*(++last_slash) = L'.';
		*(++last_slash) = L'p';
		*(++last_slash) = L'a';
		*(++last_slash) = L'c';
		*(++last_slash) = L'k';
	}
	else  //NULL
		wcscat(path,L"_OUT.pack");

	//kernel
	fileW_open(input,src,L"rb");
	fileW_open(output,path,L"wb");
	lzw_decompress(input, output), Wfclose_all(path);
}
void Encrypt(wchar_t* path, const char* password)
{
	// src_init
	wchar_t src[wcslen(path)];
	wcscpy(src,path);
	//_encrypt.lzw
	wchar_t* last_slash = wcsrchr(path, L'.');
	if (last_slash)
	{
		*last_slash = L'_';
		*(++last_slash) = L'e';
		*(++last_slash) = L'n';
		*(++last_slash) = L'c';
		*(++last_slash) = L'r';
		*(++last_slash) = L'y';
		*(++last_slash) = L'p';
		*(++last_slash) = L't';

		if (NULL != wcsstr(src,L".lzw" )) //*.lzw
		{
			*(++last_slash) = L'.';
			*(++last_slash) = L'l';
			*(++last_slash) = L'z';
			*(++last_slash) = L'w';
		}
		else if (NULL != wcsstr(src,L".pack" )) //*.pack
		{
			*(++last_slash) = L'.';
			*(++last_slash) = L'p';
			*(++last_slash) = L'a';
			*(++last_slash) = L'c';
			*(++last_slash) = L'k';
		}
		else	//default_file_format::  *.crypt
		{
			*(++last_slash) = L'.';
			*(++last_slash) = L'c';
			*(++last_slash) = L'r';
			*(++last_slash) = L'y';
			*(++last_slash) = L'p';
			*(++last_slash) = L't';
		}

	}
	else  //NULL
		wcscat(path,L"_encrypt.crypt");

	//kernel
//	process_file_W(path,path,"1092357");//test01
//	process_file_W(path,path,password);//test02
	process_file_W(src,path,password);

}
void Decrypt(wchar_t* path, const char* password)
{
	// src_init
	wchar_t src[wcslen(path)];
	wcscpy(src,path);
	//_decrypt.lzw
	wchar_t* last_slash = wcsrchr(path, L'.');
	if (last_slash)
	{
		*last_slash = L'_';
		*(++last_slash) = L'd';
		*(++last_slash) = L'e';
		*(++last_slash) = L'c';
		*(++last_slash) = L'r';
		*(++last_slash) = L'y';
		*(++last_slash) = L'p';
		*(++last_slash) = L't';

		if (NULL != wcsstr(src,L".lzw" )) //*.lzw
		{
			*(++last_slash) = L'.';
			*(++last_slash) = L'l';
			*(++last_slash) = L'z';
			*(++last_slash) = L'w';
		}
		else if (NULL != wcsstr(src,L".pack" )) //*.pack
		{
			*(++last_slash) = L'.';
			*(++last_slash) = L'p';
			*(++last_slash) = L'a';
			*(++last_slash) = L'c';
			*(++last_slash) = L'k';
		}
		else	//default_file_format::  *.crypt
		{
			*(++last_slash) = L'.';
			*(++last_slash) = L'c';
			*(++last_slash) = L'r';
			*(++last_slash) = L'y';
			*(++last_slash) = L'p';
			*(++last_slash) = L't';
		}

	}
	else  //NULL
		wcscat(path,L"_decrypt.crypt");

	//kernel
//	process_file_W(path,path,"1092357");//test01
//	process_file_W(path,path,password);//test02
	process_file_W(src,path,password);

}
